#include "tree.h"





Tree::Tree(ID3D10Device* d, int s)
{
	m_size=s;
	m_pd3dDevice = d;
	D3DXCreateMatrixStack(0,&m_matrixStack);
	
}

///<Summary> generate tree branch structures form a L-system </Summary>
HRESULT Tree::generateLSys(LSystem * system, int level, float length  )
{
	string s=system->Expand(level);
	

	char c;

	D3DXMATRIX world;
	D3DXMatrixIdentity(&world);
	
	if(this->m_BranchList.size()>0) this->m_BranchList.clear();
	if(this->m_LeafList.size()>0) this->m_LeafList.clear();
	
	stack<branch*> branchStack;
	
	branch * current = NULL;
	
	
	
	for(unsigned int i=0; i<s.length(); i++)
	{
		c=s[i];

		switch(c)
		{
		case 'F': addBranch(&world, length, &current);
				
				break;
			case '+': rotateY(DEFAULT_ANGLE,&world);
				break;
			case '-': rotateY(-DEFAULT_ANGLE,&world);
				break;
			case '<': rotateX(DEFAULT_ANGLE,&world);
				break;
			case '>': rotateX(-DEFAULT_ANGLE,&world);
				break;
			case '/': rotateZ(DEFAULT_ANGLE,&world);
				break;
			case '\\': rotateZ(-DEFAULT_ANGLE,&world);
				break;
			case '[': push(&world);
				branchStack.push(current);
				break;
			case ']': pop(&world);
				current = branchStack.top();
				branchStack.pop();
				break;
			case '|': rotateY(3.14f,&world);
				break;
			default:
				break;
			
		}

	
	}
	this->calculateThickness(&m_BranchList);

	return S_OK;
}





Tree::~Tree()
{


}


void Tree::rotateZ(float angle,D3DXMATRIX * w)
{
	 
	 D3DXMATRIX mRotation;

	 
	 D3DXMatrixRotationZ(&mRotation,angle);
	 D3DXMatrixMultiply(w, &mRotation, w);
	 

}

void Tree::rotateX(float angle,D3DXMATRIX * w)
{
	 D3DXMATRIX mRotation;

	 
	 D3DXMatrixRotationX(&mRotation,angle);
	 D3DXMatrixMultiply(w, &mRotation, w);
}

void Tree::rotateY(float angle,D3DXMATRIX * w)
{
	 D3DXMATRIX mRotation;

	 
	 D3DXMatrixRotationY(&mRotation,angle);
	 D3DXMatrixMultiply(w, &mRotation, w);

}

void Tree::push(D3DXMATRIX * w)
{
	D3DXMATRIX * tmp =(D3DXMATRIX*)malloc(sizeof(D3DXMATRIX)) ;
	memcpy(tmp, w, sizeof(D3DXMATRIX));
	m_matrixStack->Push();
	m_matrixStack->LoadMatrix(tmp);
}

void Tree::pop(D3DXMATRIX * w)
{

	D3DXMATRIX *tmp=m_matrixStack->GetTop();
	memcpy(w, tmp, sizeof(D3DXMATRIX));
	m_matrixStack->Pop();
}

void Tree::addBranch(D3DXMATRIX *w , float length, branch ** p)
{
	D3DXVECTOR4 position = D3DXVECTOR4 (0.0f, 0.0f, 0.0f, 1.0f);
	D3DXVECTOR4 direction = D3DXVECTOR4 (0.0f, 1.0f, 0.0f, 1.0f);
	
	D3DXVec4Transform(&position, &position, w);
	D3DXVec4Transform(&direction, &direction, w);


	branch * b=new branch();

	b->direction[0]=direction.x-position.x;
	b->direction[1]=direction.y-position.y;
	b->direction[2]=direction.z-position.z;

	b->position[0]=position.x;
	b->position[1]=position.y;
	b->position[2]=position.z;

	b->length=length;
	b->level=0;
	b->prev=*p;

	if(*p!=NULL)
	{
		b->prevDirection[0]=(*p)->direction[0];
		b->prevDirection[1]=(*p)->direction[1];
		b->prevDirection[2]=(*p)->direction[2];

	}
	else
	{
		b->prevDirection[0]=0.0f;
		b->prevDirection[1]=1.0f;
		b->prevDirection[2]=0.0f;

	}



	this->m_BranchList.push_back(b); 
	*p=this->m_BranchList.back();

	
	 D3DXMATRIX mTranslation;	 
	 D3DXMatrixTranslation(&mTranslation,0,length, 0);
	 D3DXMatrixMultiply(w, &mTranslation, w);

}

void Tree::calculateThickness(std::vector<branch*> * b)
{

	

	std::vector<branch*>::iterator i=b->begin();
	for(i=b->begin(); i!=b->end(); i++)
	{


		bool end=true;

		std::vector<branch*>::iterator j=b->begin();
		for(j=b->begin(); j!=b->end(); j++)
		{
			if((*j)->prev==*i)
				end=false;
		}

		if(end) 
		{

			int l=1;
			branch * current=*i;
			current->level=l;
			current->leafs=true;
			this->m_LeafList.push_back(*current);
			
			
			while(current->prev!=NULL)
			{
				current=current->prev;
				
				l++;
				if(current->level<l)
					current->level=l;
				else
					break;
				
			}
		}


	}


}




HRESULT Tree::generateLeafsCUDA(int n, float m_size)
{
	m_leafCount=n;


	

	//allocate memory for vertices and indices
	DWORD * indices=(DWORD*) malloc(this->m_LeafList.size() * sizeof(DWORD) * m_leafCount * IPL);;
	SimpleVertex * vertices=(SimpleVertex*) malloc(this->m_LeafList.size() * sizeof(SimpleVertex) * m_leafCount * VPL);
	
	
	ofstream g_log("log2.txt");
	branch * tempArray=new branch[this->m_LeafList.size()];
	std::vector<branch>::iterator j=this->m_LeafList.begin();
	int p=0;
	for(j=this->m_LeafList.begin(); j!=this->m_LeafList.end(); j++)
	{
		tempArray[p]=*j;
		p++;
	}

	generateLeafs(tempArray, this->m_LeafList.size(), n, m_size, vertices, indices, g_log);

	
	m_Leafs=new DrawableObject(m_pd3dDevice);

	//create buffers
	m_Leafs->CreateVertexBuffer(vertices,	this->m_LeafList.size() *  m_leafCount * VPL);
	m_Leafs->CreateIndexBuffer(indices, this->m_LeafList.size() *  m_leafCount * IPL,D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);	

	free(indices);
	free(vertices);


	return S_OK;
}

//Generate treee vertices and indices on CUDA
HRESULT Tree::generateCuda(int w, float r)
{

	//set no. of m_walls and allocate memory
	m_walls=w;
	SimpleVertex *v=(SimpleVertex*)malloc( this->m_BranchList.size() * w * VPW *sizeof(SimpleVertex)) ;
	DWORD *ind=(DWORD*)malloc( this->m_BranchList.size() * w * IPW *sizeof(DWORD)) ;
	
	ofstream g_log("log.txt");
	
	
	//call CUDA kernel to generate vertices	
	branch * tempArray=new branch[this->m_BranchList.size()];
	std::vector<branch*>::iterator i=this->m_BranchList.begin();
	int j=0;
	for(i=this->m_BranchList.begin(); i!=this->m_BranchList.end(); i++)
	{
		tempArray[j]=**i;
		j++;
	}
	generateTree(tempArray,this->m_BranchList.size(),w,r ,v,ind,g_log);



	m_Branches=new DrawableObject(m_pd3dDevice);

	//create buffers
	m_Branches->CreateVertexBuffer(v,this->m_BranchList.size() * w * VPW);
	m_Branches->CreateIndexBuffer(ind, this->m_BranchList.size() * w * IPW);	

	free(v);
	free(ind);
	return S_OK;
}



void Tree::renderBranches()
{
	if(m_Branches)
		m_Branches->Draw();
}

void Tree::renderLeafs()
{
	if(m_Leafs)
		m_Leafs->Draw();
}

DrawableObject * Tree::GetBranches()
{
	if(m_Branches)
		return m_Branches;
	else 
		return NULL;
}

DrawableObject * Tree::GetLeafs()
{
	if(m_Leafs)
		return m_Leafs;
	else 
		return NULL;
}
//